/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fresher.utility;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author quangtm5
 */
public class BlockingQueueConnectionPool {
    private static BlockingQueueConnectionPool blockingQueueConnectionPoolInstance = null;    
    private static BlockingQueue<ConnectionObject> connectionQueue = null;
    
    private String databaseName = "cafe";
    private String userName = "root";
    private String passWord = "root";
    
    //count existed connections in queue
    private static AtomicInteger connectionCount = new AtomicInteger();
    
    //min connection
    private static int innitializeConnection = 10;
    
    //max connection
    private static int maxConnection = 100;
    
    private BlockingQueueConnectionPool()
    {
         connectionQueue = new LinkedBlockingQueue<>();
         
         for(int i = 0 ; i < innitializeConnection ; i++)
         {
            ConnectionObject connectObject = new ConnectionObject(this.userName, this.passWord, this.databaseName);
           
            //add connection to queue
            BlockingQueueConnectionPool.addNewConnection(connectObject);
         }
         
    }
    
    public static BlockingQueueConnectionPool getBQConnectionPool()
    {
        if(blockingQueueConnectionPoolInstance == null)
        {
            blockingQueueConnectionPoolInstance = new BlockingQueueConnectionPool();
        }
        
        return blockingQueueConnectionPoolInstance;
    }
    
    /**
     * 
     * @param connectionObject 
     */
    public static void addNewConnection(ConnectionObject connectionObject)
    {
        
        try
        {
            //Inserts the specified element into this queue
            //waiting up to 100 miliseconds if necessary for space to become available
            connectionQueue.offer(connectionObject, 100, TimeUnit.MILLISECONDS);
            
            connectionCount.incrementAndGet();
        }
        catch (InterruptedException ex)
        {
            Logger.getLogger(BlockingQueueConnectionPool.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * After use connection, client must return the connection to queue
     * 
     * @param connectionObject 
     */
    public static void giveConnectionBackToQueue(ConnectionObject connectionObject)
    {
        if(connectionObject != null)
        {
            try
            {
                connectionQueue.offer(connectionObject, 100, TimeUnit.MILLISECONDS);
                
            }
            catch (InterruptedException ex)
            {
                Logger.getLogger(BlockingQueueConnectionPool.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
//        System.out.println("POLL CONNECTION giveConnectionBackToQueue() : " + connectionQueue.size());
    }
    
    /**
     * 
     * @return
     * @throws Exception 
     */
    public ConnectionObject getConnectionObject() throws Exception
    {
        ConnectionObject conn = null;
        try
        {
            //just wait for 5 miliseconds to find an available connection 
            conn = connectionQueue.poll(5, TimeUnit.MILLISECONDS);
            
            System.out.println("POOL CONNECTION getConnectionObject() : " + connectionQueue.size());
            
            //if can't find an availabe connection 
            //try to create a new connection to use
            if(conn == null)
            {
                //just create one by one connection
                //to check the maxConnection
                synchronized (connectionCount)
                {
                    if(connectionCount.get() < maxConnection)
                    {
                        conn = new ConnectionObject(this.userName, this.passWord, this.databaseName);
                        
                        connectionQueue.offer(conn,100, TimeUnit.MILLISECONDS);
                        
                        connectionCount.incrementAndGet();
                    }
                    
                    if(conn == null)
                    {
                        throw  new Exception("BlockingQueueConnectionPool - getConnectionObject() : CONNECTION IS STILL NULL.");
                    }
                    else
                    {
                        return connectionQueue.poll();
                    }
                }
            }
        }
        catch (InterruptedException ex)
        {
            Logger.getLogger(BlockingQueueConnectionPool.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return conn;
    }
}
